from django_redis import get_redis_connection
from rest_framework import serializers

from goods.models import SKU
from order.models import OrderInfo, OrderGoods


class PlaceOrderSerializer(serializers.ModelSerializer):

    count = serializers.IntegerField(label='个数')

    class Meta:
        model = SKU
        fields = ['id','name','price','default_image_url','count']


class PlaceSerializer(serializers.Serializer):

    skus = PlaceOrderSerializer(many=True)
    # max_digits        最大长度
    # decimal_places     小数点的位数
    freight = serializers.DecimalField(max_digits=10,decimal_places=2)


class OrderSerializer(serializers.ModelSerializer):

    class Meta:
        model = OrderInfo
        fields = ('order_id','address','pay_method')
        read_only_fields = ('order_id')
        extra_kwargs = {
            'address':{
                'write_only':True,
                'required':True,
            },
            'pay_method':{
                'write_only':True,
                'required':True,
            }
        }

    """
        前端只是将 用户的token，address，pay方式提交给了我们
        我们需要实现 订单信息 以及 订单商品列表数据 的入库

        一、先实现 订单信息的保存
            1、先接受地址信息
            2、接受支付方式
            3、接收用户信息
            4、生成订单id
            5、订单总数量和 总价格（先为0）以及运费
            6、根据支付方式决定状态
        二、再去实现 订单商品列表的保存
            1、链接redis
            2、获取redis的数据hash，set
            3、对redis数据进行转换
                在转换过程中只转换 选中的信息
            4、对选中商品信息的列表进行遍历
            5、根据商品id进行查询
            6、查询出来之后我们要根据商品的数量进行判断，库存是否充足
            7、库存减少，销量增加
            8、遍历的过程中，累加商品数量和总价格
            9、商品数据入库

    """
    def create(self, validated_data):
        # 一、先实现订单信息的保存
        #     1、先接受地址信息
        address = validated_data['address']
        #     2、接受支付方式
        pay_method = validated_data['pay_method']
        #     3、接收用户信息
        user = self.context['request'].user
        #     4、生成订单id
        #   年月日时分秒 + 9位用户id信息
        from django.utils import timezone
            # Year,年
            # month,月
            # day,日
            # Hour 小时
            # Minute 分钟
            # Second 秒
        order_id = timezone.now().strftime('%Y%m%d%H%M%S') + '%09d'%user.id
        #     5、订单总数量和 总价格（先为0）以及运费
        total_count = 0
        from _decimal import Decimal
        total_amount = Decimal('0') #总价格
        freight = Decimal('10')  #运费
        #     6、根据支付方式决定状态
        #       如果是 货到付款 则应该是  待发货
        #       如果是 支付宝  则应该是   待付款
        if pay_method == OrderInfo.PAY_METHODS_ENUM['CASH']:
            status = OrderInfo.ORDER_STATUS_ENUM['UNSEND']
        else:
            status = OrderInfo.ORDER_STATUS_ENUM['UNPAID']

        from django.db import transaction
        with transaction.atomic():
            # 如果出现问题，则回滚到这里，没有创建订单前
            save_point = transaction.savepoint()

        order = OrderInfo.objects.create(
            order_id=order_id,
            user=user,
            address=address,
            total_count=total_count,
            total_amount=total_amount,
            freight=freight,
            pay_method=pay_method,
            status=status
        )

        # 二、再去实现 订单商品列表的保存
        #     1、链接redis
        redis_conn = get_redis_connection('cart')
        #     2、获取redis的数据hash，set
        sku_id_counts = redis_conn.hgetall('cart_%s'%user.id)
        selected_ids = redis_conn.smembers('cart_selected_%s'%user.id)

        #     3、对redis数据进行转换
        #         在转换过程中只转换 选中的信息
        redis_selected_cart = {}
        for sku_id in selected_ids:
            redis_selected_cart[int(sku_id)]=int(sku_id_counts[sku_id])
        #     4、对选中商品信息的列表进行遍历
        for sku_id,count in redis_selected_cart.items():
            #     5、根据商品id进行查询
            try:
                sku = SKU.objects.get(pk=sku_id)
            except SKU.DoesNotExist:
                # 回滚
                transaction.savepoint_rollback(save_point)
                # 订单下单失败
                raise serializers.ValidationError('商品不存在')
            #     6、查询出来之后我们要根据商品的数量进行判断，库存是否充足
            if sku.stock < count:
                transaction.savepoint_rollback(save_point)
                # 库存不足
                raise serializers.ValidationError('库存不足')
            #     7、库存减少，销量增加

            """
            并发处理的问题

            1、悲观锁：当查询某条记录时，即让数据库为该记录加锁，锁住记录后别人无法操作
                    容易造成 死锁，不介意被使用

                    甲乙同时下单

                    甲   A   去修改B, 发现B在被修改,它就等着

                    乙   B   去修改A, 发现A在被修改,它就等着

            2、乐观锁：乐观锁并不是真实存在的锁，而是在更新的时候判断此时的库存是否是之前查询出的库存，
                    如果相同，表示没人修改，可以更新库存，否则表示别人抢过资源，不再执行库存更新

                    肉包子:  25个肉包子
                     甲: 25个   现在再看25个,甲就吃10个, 剩15个
                     乙: 25个   再看的时候就是 15个了,不更新了

            """
            # 模拟并发
            import time
            time.sleep(7)

            # ①先查询一个库存
            old_stock = sku.stock
            # ②计算更新之后的数据
            new_stock = sku.stock-count #库存
            new_sales = sku.sales+count #销量
            # ③更新数据前 在查询一次，看现在的数据是否和之前记录的一致
            #  一致则更新，不一致则不更新

            # 如果更新成功则返回1
            #如果更新不成功则返回0
            rect = SKU.objects.filter(pk=sku.id,stock=old_stock).update(stock=new_stock,sales=new_sales)

            if rect == 0:
                #表示下单失败，下单失败需要回滚，同时抛出异常
                transaction.savepoint_rollback(save_point)
                raise serializers.ValidationError('下单失败')
            # sku.stock -= count
            # sku.sales += count
            # sku.save()
            #     8、遍历的过程中，累加商品数量和总价格
            order.total_count += count
            order.total_amount += (count*sku.price)
            #     9、商品数据入库
            OrderGoods.objects.create(
                order=order,
                sku=sku,
                count=count,
                price=sku.price
            )

        # 都遍历完成之后，我们只写入数据一次
        order.save()
        #没有任何问题就可以提交数据
        transaction.savepoint_commit(save_point)

        # 清楚redis中选中商品的信息
        return order
















































